#ifndef _WX_WXSTRINGH__
#define _WX_WXSTRINGH__

#include "wx/defs.h"        // everybody should include this
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <limits.h>
#include <stdlib.h>

#ifdef HAVE_STRCASECMP_IN_STRINGS_H
#include <strings.h>    // for strcasecmp()
#endif // HAVE_STRCASECMP_IN_STRINGS_H


#include "wx/wxchar.h"      // for wxChar
#include "wx/buffer.h"      // for wxCharBuffer
#include "wx/strconv.h"     // for wxConvertXXX() macros and wxMBConv classes

class wxString;

#define   WXSTRINGCAST (wxChar *)(const wxChar *)
#define   wxCSTRINGCAST (wxChar *)(const wxChar *)
#define   wxMBSTRINGCAST (char *)(const char *)
#define   wxWCSTRINGCAST (wchar_t *)(const wchar_t *)

// implementation only
#define   wxASSERT_VALID_INDEX(i) \
  wxASSERT_MSG( (size_t)(i) <= length(), wxT("invalid index in wxString") )

#define wxSTRING_MAXLEN wxStringBase::npos

extern  const wxChar* wxEmptyString;
inline bool IsEmpty( const char *p ) { return ( !p || !*p ); }

inline size_t Strlen( const char *psz ) {
  return psz ? strlen( psz ) : 0;
}

// portable strcasecmp/_stricmp
inline int Stricmp( const char *psz1, const char *psz2 ) {
  #if defined(__VISUALC__) && defined(__WXWINCE__)
  register char c1, c2;
  do {
    c1 = tolower( *psz1++ );
    c2 = tolower( *psz2++ );
  } while( c1 && ( c1 == c2 ) );
  return c1 - c2;
  #elif defined(__VISUALC__) || ( defined(__MWERKS__) && defined(__INTEL__) )
  return _stricmp( psz1, psz2 );
  #elif defined(__SC__)
  return _stricmp( psz1, psz2 );
  #elif defined(__SALFORDC__)
  return stricmp( psz1, psz2 );
  #elif defined(__BORLANDC__)
  return stricmp( psz1, psz2 );
  #elif defined(__WATCOMC__)
  return stricmp( psz1, psz2 );
  #elif defined(__DJGPP__)
  return stricmp( psz1, psz2 );
  #elif defined(__EMX__)
  return stricmp( psz1, psz2 );
  #elif defined(__WXPM__)
  return stricmp( psz1, psz2 );
  #elif defined(__WXPALMOS__) || \
  defined(HAVE_STRCASECMP_IN_STRING_H) || \
  defined(HAVE_STRCASECMP_IN_STRINGS_H) || \
  defined(__GNUWIN32__)
  return strcasecmp( psz1, psz2 );
  #elif defined(__MWERKS__) && !defined(__INTEL__)
  register char c1, c2;
  do {
    c1 = tolower( *psz1++ );
    c2 = tolower( *psz2++ );
  } while( c1 && ( c1 == c2 ) );
  return c1 - c2;
  #else
#error  "Please define string case-insensitive compare for your OS/compiler"
  #endif  // OS/compiler
}

#if wxUSE_STL || wxUSE_STD_STRING

#include "wx/beforestd.h"
#include <string>
#include "wx/afterstd.h"

#if wxUSE_UNICODE
#ifdef HAVE_STD_WSTRING
typedef std::wstring wxStdString;
#else
typedef std::basic_string<wxChar> wxStdString;
#endif
#else
typedef std::string wxStdString;
#endif

#endif // need <string>


#if !defined(HAVE_STD_STRING_COMPARE) && \
(!defined(__WX_SETUP_H__) || wxUSE_STL == 0)
#define HAVE_STD_STRING_COMPARE
#endif

// ---------------------------------------------------------------------------
// string data prepended with some housekeeping info (used by wxString class),
// is never used directly (but had to be put here to allow inlining)
// ---------------------------------------------------------------------------

struct wxStringData {
  int     nRefs;        // reference count
  size_t  nDataLength,  // actual string length
          nAllocLength; // allocated memory size

  // mimics declaration 'wxChar data[nAllocLength]'
  wxChar* data() const { return ( wxChar* )( this + 1 ); }

  // empty string has a special ref count so it's never deleted
  bool  IsEmpty()   const { return ( nRefs == -1 ); }
  bool  IsShared()  const { return ( nRefs > 1 );   }

  // lock/unlock
  void  Lock()   { if( !IsEmpty() ) nRefs++;                    }

  // VC++ will refuse to inline Unlock but profiling shows that it is wrong
  #if defined(__VISUALC__) && (__VISUALC__ >= 1200)
  __forceinline
  #endif
  // VC++ free must take place in same DLL as allocation when using non dll
  // run-time library (e.g. Multithreaded instead of Multithreaded DLL)
  #if defined(__VISUALC__) && defined(_MT) && !defined(_DLL)
  void  Unlock() { if( !IsEmpty() && --nRefs == 0 ) Free();  }
  // we must not inline deallocation since allocation is not inlined
  void  Free();
  #else
  void  Unlock() { if( !IsEmpty() && --nRefs == 0 ) free( this );  }
  #endif

  void  Validate( bool b )  { nRefs = ( b ? 1 : 0 ); }
  bool  IsValid() const   { return ( nRefs != 0 ); }
};

class wxStringBase {
    #if !wxUSE_STL
    friend class  wxArrayString;
    #endif
  public :
    static const size_t npos;
  protected:
    wxChar *m_pchData;
    wxStringData* GetStringData() const { return ( wxStringData* )m_pchData - 1; }
    void Init() { m_pchData = ( wxChar * )wxEmptyString; }
    void InitWith( const wxChar *psz, size_t nPos = 0, size_t nLen = npos );
    void Reinit() { GetStringData()->Unlock(); Init(); }
    bool AllocBuffer( size_t nLen );
    bool AllocCopy( wxString&, int, int ) const;
    bool AssignCopy( size_t, const wxChar * );
    bool ConcatSelf( size_t nLen, const wxChar *src, size_t nMaxLen );
    bool ConcatSelf( size_t nLen, const wxChar *src ) { return ConcatSelf( nLen, src, nLen ); }
    bool CopyBeforeWrite();
    bool AllocBeforeWrite( size_t );
    bool Alloc( size_t nLen );
  public:
    typedef wxChar value_type;
    typedef wxChar char_type;
    typedef size_t size_type;
    typedef value_type& reference;
    typedef const value_type& const_reference;
    typedef value_type* pointer;
    typedef const value_type* const_pointer;
    typedef value_type *iterator;
    typedef const value_type *const_iterator;

#define wxSTRING_REVERSE_ITERATOR(name, const_or_not)                         \
  class name                                                                  \
  {                                                                           \
    public:                                                                     \
      typedef wxChar value_type;                                              \
      typedef const_or_not value_type& reference;                             \
      typedef const_or_not value_type *pointer;                               \
      typedef const_or_not value_type *iterator_type;                         \
      \
      name(iterator_type i) : m_cur(i) { }                                    \
      name(const name& ri) : m_cur(ri.m_cur) { }                              \
      \
      iterator_type base() const { return m_cur; }                            \
      \
      reference operator*() const { return *(m_cur - 1); }                    \
      \
      name& operator++() { --m_cur; return *this; }                           \
      name operator++(int) { name tmp = *this; --m_cur; return tmp; }         \
      name& operator--() { ++m_cur; return *this; }                           \
      name operator--(int) { name tmp = *this; ++m_cur; return tmp; }         \
      \
      bool operator==(name ri) const { return m_cur == ri.m_cur; }            \
      bool operator!=(name ri) const { return !(*this == ri); }               \
      \
    private:                                                                    \
      iterator_type m_cur;                                                    \
  }

    wxSTRING_REVERSE_ITERATOR( const_reverse_iterator, const );

#define wxSTRING_CONST
    wxSTRING_REVERSE_ITERATOR( reverse_iterator, wxSTRING_CONST );
#undef wxSTRING_CONST

#undef wxSTRING_REVERSE_ITERATOR

    wxStringBase() { Init(); }
    wxStringBase( const wxStringBase& stringSrc ) {
      wxASSERT_MSG( stringSrc.GetStringData()->IsValid(),
                    wxT( "did you forget to call UngetWriteBuf()?" ) );
      if( stringSrc.empty() ) {
        Init();
      } else {
        m_pchData = stringSrc.m_pchData;
        GetStringData()->Lock();
      }
    }
    wxStringBase( size_type nRepeat, wxChar ch );
    wxStringBase( const wxChar *psz ) { InitWith( psz, 0, npos ); }
    wxStringBase( const wxChar *psz, size_t nLength ) { InitWith( psz, 0, nLength ); }
    wxStringBase( const wxChar *psz,
                  const wxMBConv & ( conv ),
                  size_t nLength = npos ) { InitWith( psz, 0, nLength ); }
    wxStringBase( const wxStringBase& str, size_t nPos, size_t nLen ) {
      wxASSERT_MSG( str.GetStringData()->IsValid(),
                    wxT( "did you forget to call UngetWriteBuf()?" ) );
      Init();
      size_t strLen = str.length() - nPos;
      nLen = strLen < nLen ? strLen : nLen;
      InitWith( str.c_str(), nPos, nLen );
    }
    wxStringBase( const void *pStart, const void *pEnd );
    ~wxStringBase() {
      #if defined(__VISUALC__) && (__VISUALC__ >= 1200)
#pragma warning (disable:4714)
      #endif
      GetStringData()->Unlock();
    }

    #if defined(__VISUALC__) && (__VISUALC__ >= 1200)
#pragma warning (default:4714)
    #endif
    wxStringBase& operator=( const wxStringBase& stringSrc );
    wxStringBase& operator=( wxChar ch );
    wxStringBase& operator=( const wxChar *psz );
    size_type length() const { return GetStringData()->nDataLength; }
    size_type size() const { return length(); }
    size_type max_size() const { return npos; }
    void resize( size_t nSize, wxChar ch = wxT( '\0' ) );
    void clear() { erase( 0, npos ); }
    bool empty() const { return length() == 0; }
    void reserve( size_t sz ) { Alloc( sz ); }
    size_type capacity() const { return GetStringData()->nAllocLength; }
    value_type at( size_type n ) const
    { wxASSERT_VALID_INDEX( n ); return m_pchData[n]; }
    reference at( size_type n ) { wxASSERT_VALID_INDEX( n ); CopyBeforeWrite(); return m_pchData[n]; }
    wxStringBase& append( const wxStringBase& str, size_t pos, size_t n ) {
      wxASSERT( pos <= str.length() );
      ConcatSelf( n, str.c_str() + pos, str.length() - pos );
      return *this;
    }
    wxStringBase& append( const wxStringBase& str ) { ConcatSelf( str.length(), str.c_str() ); return *this; }
    wxStringBase& append( const wxChar *sz ) { ConcatSelf( wxStrlen( sz ), sz ); return *this; }
    wxStringBase& append( const wxChar *sz, size_t n ) { ConcatSelf( n, sz ); return *this; }
    wxStringBase& append( size_t n, wxChar ch );
    wxStringBase& append( const_iterator first, const_iterator last ) { ConcatSelf( last - first, first ); return *this; }
    wxStringBase& assign( const wxStringBase& str ) { return *this = str; }
    wxStringBase& assign( const wxStringBase& str, size_t pos, size_t n ) { clear(); return append( str, pos, n ); }
    wxStringBase& assign( const wxChar *sz ) { clear(); return append( sz, wxStrlen( sz ) ); }
    wxStringBase& assign( const wxChar *sz, size_t n ) { clear(); return append( sz, n ); }
    wxStringBase& assign( size_t n, wxChar ch ) { clear(); return append( n, ch ); }
    wxStringBase& assign( const_iterator first, const_iterator last ) { clear(); return append( first, last ); }
    const_iterator begin() const { return m_pchData; }
    iterator begin();
    const_iterator end() const { return m_pchData + length(); }
    iterator end();
    const_reverse_iterator rbegin() const { return const_reverse_iterator( end() ); }
    reverse_iterator rbegin() { return reverse_iterator( end() ); }
    const_reverse_iterator rend() const { return const_reverse_iterator( begin() ); }
    reverse_iterator rend() { return reverse_iterator( begin() ); }
    wxStringBase& insert( size_t nPos, const wxStringBase& str ) {
      wxASSERT( str.GetStringData()->IsValid() );
      return insert( nPos, str.c_str(), str.length() );
    }
    wxStringBase& insert( size_t nPos, const wxStringBase& str, size_t nStart, size_t n ) {
      wxASSERT( str.GetStringData()->IsValid() );
      wxASSERT( nStart < str.length() );
      size_t strLen = str.length() - nStart;
      n = strLen < n ? strLen : n;
      return insert( nPos, str.c_str() + nStart, n );
    }
    wxStringBase& insert( size_t nPos, const wxChar *sz, size_t n = npos );
    wxStringBase& insert( size_t nPos, size_t n, wxChar ch ) { return insert( nPos, wxStringBase( n, ch ) ); }
    iterator insert( iterator it, wxChar ch ) { size_t idx = it - begin(); insert( idx, 1, ch ); return begin() + idx; }
    void insert( iterator it, const_iterator first, const_iterator last ) { insert( it - begin(), first, last - first ); }
    void insert( iterator it, size_type n, wxChar ch ) { insert( it - begin(), n, ch ); }
    wxStringBase& erase( size_type pos = 0, size_type n = npos );
    iterator erase( iterator first, iterator last ) {
      size_t idx = first - begin();
      erase( idx, last - first );
      return begin() + idx;
    }
    iterator erase( iterator first );
    const wxChar* c_str() const { return m_pchData; }
    const wxChar* data() const { return m_pchData; }
    wxStringBase& replace( size_t nStart, size_t nLen, const wxChar* sz );
    wxStringBase& replace( size_t nStart, size_t nLen, const wxStringBase& str ) { return replace( nStart, nLen, str.c_str() ); }
    wxStringBase& replace( size_t nStart, size_t nLen, size_t nCount, wxChar ch );
    wxStringBase& replace( size_t nStart, size_t nLen,
                           const wxStringBase& str, size_t nStart2, size_t nLen2 );
    wxStringBase& replace( size_t nStart, size_t nLen,
                           const wxChar* sz, size_t nCount );
    wxStringBase& replace( iterator first, iterator last, const_pointer s ) { return replace( first - begin(), last - first, s ); }
    wxStringBase& replace( iterator first, iterator last, const_pointer s,
                           size_type n ) { return replace( first - begin(), last - first, s, n ); }
    wxStringBase& replace( iterator first, iterator last, const wxStringBase& s ) { return replace( first - begin(), last - first, s ); }
    wxStringBase& replace( iterator first, iterator last, size_type n, wxChar c ) { return replace( first - begin(), last - first, n, c ); }
    wxStringBase& replace( iterator first, iterator last,
                           const_iterator first1, const_iterator last1 ) { return replace( first - begin(), last - first, first1, last1 - first1 ); }
    void swap( wxStringBase& str );
    size_t find( const wxStringBase& str, size_t nStart = 0 ) const;
    size_t find( const wxChar* sz, size_t nStart = 0, size_t n = npos ) const;
    size_t find( wxChar ch, size_t nStart = 0 ) const;
    size_t rfind( const wxStringBase& str, size_t nStart = npos ) const;
    size_t rfind( const wxChar* sz, size_t nStart = npos, size_t n = npos ) const;
    size_t rfind( wxChar ch, size_t nStart = npos ) const;
    size_t find_first_of( const wxStringBase& str, size_t nStart = 0 ) const
    { return find_first_of( str.c_str(), nStart ); }
    size_t find_first_of( const wxChar* sz, size_t nStart = 0 ) const;
    size_t find_first_of( const wxChar* sz, size_t nStart, size_t n ) const;
    size_t find_first_of( wxChar c, size_t nStart = 0 ) const
    { return find( c, nStart ); }
    size_t find_last_of( const wxStringBase& str, size_t nStart = npos ) const
    { return find_last_of( str.c_str(), nStart ); }
    size_t find_last_of( const wxChar* sz, size_t nStart = npos ) const;
    size_t find_last_of( const wxChar* sz, size_t nStart, size_t n ) const;
    size_t find_last_of( wxChar c, size_t nStart = npos ) const
    { return rfind( c, nStart ); }
    size_t find_first_not_of( const wxStringBase& str, size_t nStart = 0 ) const
    { return find_first_not_of( str.c_str(), nStart ); }
    size_t find_first_not_of( const wxChar* sz, size_t nStart = 0 ) const;
    size_t find_first_not_of( const wxChar* sz, size_t nStart, size_t n ) const;
    size_t find_first_not_of( wxChar ch, size_t nStart = 0 ) const;
    size_t find_last_not_of( const wxStringBase& str, size_t nStart = npos ) const
    { return find_last_not_of( str.c_str(), nStart ); }
    size_t find_last_not_of( const wxChar* sz, size_t nStart = npos ) const;
    size_t find_last_not_of( const wxChar* sz, size_t nStart, size_t n ) const;
    size_t find_last_not_of( wxChar ch, size_t nStart = npos ) const;
    int compare( const wxStringBase& str ) const;
    int compare( size_t nStart, size_t nLen, const wxStringBase& str ) const;
    int compare( size_t nStart, size_t nLen,
                 const wxStringBase& str, size_t nStart2, size_t nLen2 ) const;
    int compare( const wxChar* sz ) const;
    int compare( size_t nStart, size_t nLen, const wxChar* sz, size_t nCount = npos ) const;
    size_type copy( wxChar* s, size_type n, size_type pos = 0 );
    wxStringBase substr( size_t nStart = 0, size_t nLen = npos ) const;
    wxStringBase& operator+=( const wxStringBase& s ) { return append( s ); }
    wxStringBase& operator+=( const wxChar *psz ) { return append( psz ); }
    wxStringBase& operator+=( wxChar ch ) { return append( 1, ch ); }
};


class wxString : public wxStringBase {
    friend class  wxArrayString;
  private:
    wxString& operator=( int );
    wxString( int );
  public:
    wxString() : wxStringBase() { }
    wxString( const wxStringBase& stringSrc ) : wxStringBase( stringSrc ) { }
    wxString( const wxString& stringSrc ) : wxStringBase( stringSrc ) { }
    wxString( wxChar ch, size_t nRepeat = 1 )
      : wxStringBase( nRepeat, ch ) { }
    wxString( size_t nRepeat, wxChar ch )
      : wxStringBase( nRepeat, ch ) { }
    wxString( const wxChar *psz )
      : wxStringBase( psz ? psz : wxT( "" ) ) { }
    wxString( const wxChar *psz, size_t nLength )
      : wxStringBase( psz, nLength ) { }
    wxString( const wxChar *psz,
              const wxMBConv & ( conv ),
              size_t nLength = npos )
      : wxStringBase( psz, nLength == npos ? wxStrlen( psz ) : nLength ) { }

    wxString( const wxStdString& s )
      : wxStringBase( s.c_str() ) { }
    wxString( const char *psz, const wxMBConv& conv, size_t nLength = npos );
    wxString( const wxWCharBuffer& psz ) : wxStringBase( psz.data() ) { }
    size_t Len() const { return length(); }
    // string contains any characters?
    bool IsEmpty() const { return empty(); }
    // empty string is "false", so !str will return true
    bool operator!() const { return empty(); }
    // truncate the string to given length
    wxString& Truncate( size_t uiLen );
    // empty string contents
    void Empty() {
      Truncate( 0 );
      wxASSERT_MSG( empty(), wxT( "string not empty after call to Empty()?" ) );
    }
    // empty the string and free memory
    void Clear() {
      wxString tmp( wxEmptyString );
      swap( tmp );
    }

    // contents test
    // Is an ascii value
    bool IsAscii() const;
    // Is a number
    bool IsNumber() const;
    // Is a word
    bool IsWord() const;

    // data access (all indexes are 0 based)
    // read access
    wxChar  GetChar( size_t n ) const
    { return at( n ); }
    // read/write access
    wxChar& GetWritableChar( size_t n ) { return at( n ); }
    // write access
    void  SetChar( size_t n, wxChar ch ) { at( n ) = ch; }

    // get last character
    wxChar  Last() const {
      wxASSERT_MSG( !empty(), wxT( "wxString: index out of bounds" ) );
      return at( length() - 1 );
    }

    // get writable last character
    wxChar& Last() {
      wxASSERT_MSG( !empty(), wxT( "wxString: index out of bounds" ) );
      return at( length() - 1 );
    }
    #if defined(__BORLANDC__) || defined(__WATCOMC__) || defined(__MWERKS__)
    wxChar operator[]( int n ) const
    { return wxStringBase::at( n ); }
    wxChar operator[]( size_type n ) const
    { return wxStringBase::at( n ); }
    #ifndef wxSIZE_T_IS_UINT
    wxChar operator[]( unsigned int n ) const
    { return wxStringBase::at( n ); }
    #endif // size_t != unsigned int
    #endif // broken compiler


    // operator versions of GetWriteableChar()
    wxChar& operator[]( int n ) { return wxStringBase::at( n ); }
    wxChar& operator[]( size_type n ) { return wxStringBase::at( n ); }
    #ifndef wxSIZE_T_IS_UINT
    wxChar& operator[]( unsigned int n ) { return wxStringBase::at( n ); }
    #endif // size_t != unsigned int

    // implicit conversion to C string
    operator const wxChar*() const { return c_str(); }

    // identical to c_str(), for wxWin 1.6x compatibility
    const wxChar* wx_str()  const { return c_str(); }
    // identical to c_str(), for MFC compatibility
    const wxChar* GetData() const { return c_str(); }
    wxWritableCharBuffer char_str( const wxMBConv& conv = wxConvLibc ) const
    { return mb_str( conv ); }
    #if wxUSE_WCHAR_T
    wxWritableWCharBuffer wchar_str() const { return wc_str( wxConvLibc ); }
    #endif

    static wxString FromAscii( const char *ascii ); // string
    static wxString FromAscii( const char ascii ); // char
    const wxCharBuffer ToAscii() const;
    static wxString FromUTF8( const char *utf8 ) { return wxString( utf8, wxConvUTF8 ); }
    static wxString FromUTF8( const char *utf8, size_t len ) { return wxString( utf8, wxConvUTF8, len ); }
    const wxCharBuffer utf8_str() const { return mb_str( wxConvUTF8 ); }
    const wxCharBuffer ToUTF8() const { return utf8_str(); }
    static wxString From8BitData( const char *data, size_t len ) { return wxString( data, wxConvISO8859_1, len ); }
    // version for NUL-terminated data:
    static wxString From8BitData( const char *data ) { return wxString( data, wxConvISO8859_1 ); }
    const wxCharBuffer To8BitData() const { return mb_str( wxConvISO8859_1 ); }

    const wxCharBuffer mb_str( const wxMBConv& conv = wxConvLibc ) const;

    const wxWX2MBbuf mbc_str() const { return mb_str( *wxConvCurrent ); }

    const wxChar* wc_str() const { return c_str(); }

    // for compatibility with !wxUSE_UNICODE version
    const wxChar* wc_str( const wxMBConv & ( conv ) ) const { return c_str(); }

    #if wxMBFILES
    const wxCharBuffer fn_str() const { return mb_str( wxConvFile ); }
    #else // !wxMBFILES
    const wxChar* fn_str() const { return c_str(); }
    #endif // wxMBFILES/!wxMBFILES
    wxString& operator=( const wxStringBase& stringSrc ) { return ( wxString& )wxStringBase::operator=( stringSrc ); }
    // from a character
    wxString& operator=( wxChar ch ) { return ( wxString& )wxStringBase::operator=( ch ); }
    // from a C string - STL probably will crash on NULL,
    // so we need to compensate in that case
    wxString& operator=( const wxChar *psz ) { return ( wxString& )wxStringBase::operator=( psz ); }

    wxString& operator=( const wxWCharBuffer& psz ) { ( void ) operator=( ( const wchar_t * )psz ); return *this; }

    wxString& operator<<( const wxString& s ) {
      wxASSERT_MSG( s.GetStringData()->IsValid(), wxT( "did you forget to call UngetWriteBuf()?" ) );
      append( s );
      return *this;
    }
    // string += C string
    wxString& operator<<( const wxChar *psz ) { append( psz ); return *this; }
    // string += char
    wxString& operator<<( wxChar ch ) { append( 1, ch ); return *this; }
    wxString& operator<<( const wxWCharBuffer& s ) { ( void )operator<<( ( const wchar_t * )s ); return *this; }
    void operator+=( const wxWCharBuffer& s ) { ( void )operator<<( ( const wchar_t * )s ); }
    wxString& Append( const wxString& s ) {
      if( empty() ) {
        *this = s;
      } else
      { append( s ); }
      return *this;
    }
    wxString& Append( const wxChar* psz ) { append( psz ); return *this; }
    // append count copies of given character
    wxString& Append( wxChar ch, size_t count = 1u ) { append( count, ch ); return *this; }
    wxString& Append( const wxChar* psz, size_t nLen ) { append( psz, nLen ); return *this; }

    // prepend a string, return the string itself
    wxString& Prepend( const wxString& str ) { *this = str + *this; return *this; }

    // non-destructive concatenation
    // two strings
    friend wxString operator+( const wxString& string1,
                               const wxString& string2 );
    // string with a single char
    friend wxString operator+( const wxString& string, wxChar ch );
    // char with a string
    friend wxString operator+( wxChar ch, const wxString& string );
    // string with C string
    friend wxString operator+( const wxString& string,
                               const wxChar *psz );
    // C string with string
    friend wxString operator+( const wxChar *psz,
                               const wxString& string );

    // stream-like functions
    // insert an int into string
    wxString& operator<<( int i ) { return ( *this ) << Format( wxT( "%d" ), i ); }
    // insert an unsigned int into string
    wxString& operator<<( unsigned int ui ) { return ( *this ) << Format( wxT( "%u" ), ui ); }
    // insert a long into string
    wxString& operator<<( long l ) { return ( *this ) << Format( wxT( "%ld" ), l ); }
    // insert an unsigned long into string
    wxString& operator<<( unsigned long ul ) { return ( *this ) << Format( wxT( "%lu" ), ul ); }
    #if defined wxLongLong_t && !defined wxLongLongIsLong
    // insert a long long if they exist and aren't longs
    wxString& operator<<( wxLongLong_t ll ) {
      const wxChar *fmt = wxT( "%" ) wxLongLongFmtSpec wxT( "d" );
      return ( *this ) << Format( fmt, ll );
    }
    // insert an unsigned long long
    wxString& operator<<( wxULongLong_t ull ) {
      const wxChar *fmt = wxT( "%" ) wxLongLongFmtSpec wxT( "u" );
      return ( *this ) << Format( fmt, ull );
    }
    #endif
    // insert a float into string
    wxString& operator<<( float f ) { return ( *this ) << Format( wxT( "%f" ), f ); }
    // insert a double into string
    wxString& operator<<( double d ) { return ( *this ) << Format( wxT( "%g" ), d ); }

    // string comparison
    // case-sensitive comparison (returns a value < 0, = 0 or > 0)
    int Cmp( const wxChar *psz ) const;
    int Cmp( const wxString& s ) const;
    // same as Cmp() but not case-sensitive
    int CmpNoCase( const wxChar *psz ) const;
    int CmpNoCase( const wxString& s ) const;
    // test for the string equality, either considering case or not
    // (if compareWithCase then the case matters)
    bool IsSameAs( const wxChar *psz, bool compareWithCase = true ) const
    { return ( compareWithCase ? Cmp( psz ) : CmpNoCase( psz ) ) == 0; }
    // comparison with a single character: returns true if equal
    bool IsSameAs( wxChar c, bool compareWithCase = true ) const {
      return ( length() == 1 ) && ( compareWithCase ? GetChar( 0u ) == c
                                    : towupper( GetChar( 0u ) ) == towupper( c ) );
    }

    // simple sub-string extraction
    // return substring starting at nFirst of length nCount (or till the end
    // if nCount = default value)
    wxString Mid( size_t nFirst, size_t nCount = npos ) const;

    // operator version of Mid()
    wxString  operator()( size_t start, size_t len ) const
    { return Mid( start, len ); }

    bool StartsWith( const wxChar *prefix, wxString *rest = NULL ) const;
    // check if the string ends with the given suffix and return the
    // beginning of the string before the suffix in the provided pointer if
    // it is not NULL; otherwise return false
    bool EndsWith( const wxChar *suffix, wxString *rest = NULL ) const;

    // get first nCount characters
    wxString Left( size_t nCount ) const;
    // get last nCount characters
    wxString Right( size_t nCount ) const;
    // get all characters before the first occurance of ch
    // (returns the whole string if ch not found)
    wxString BeforeFirst( wxChar ch ) const;
    // get all characters before the last occurence of ch
    // (returns empty string if ch not found)
    wxString BeforeLast( wxChar ch ) const;
    // get all characters after the first occurence of ch
    // (returns empty string if ch not found)
    wxString AfterFirst( wxChar ch ) const;
    // get all characters after the last occurence of ch
    // (returns the whole string if ch not found)
    wxString AfterLast( wxChar ch ) const;

    // for compatibility only, use more explicitly named functions above
    wxString Before( wxChar ch ) const { return BeforeLast( ch ); }
    wxString After( wxChar ch ) const { return AfterFirst( ch ); }

    // case conversion
    // convert to upper case in place, return the string itself
    wxString& MakeUpper();
    // convert to upper case, return the copy of the string
    // Here's something to remember: BC++ doesn't like returns in inlines.
    wxString Upper() const ;
    // convert to lower case in place, return the string itself
    wxString& MakeLower();
    // convert to lower case, return the copy of the string
    wxString Lower() const ;

    // trimming/padding whitespace (either side) and truncating
    // remove spaces from left or from right (default) side
    wxString& Trim( bool bFromRight = true );
    // add nCount copies chPad in the beginning or at the end (default)
    wxString& Pad( size_t nCount, wxChar chPad = wxT( ' ' ), bool bFromRight = true );

    // searching and replacing
    // searching (return starting index, or -1 if not found)
    int Find( wxChar ch, bool bFromEnd = false ) const; // like strchr/strrchr
    // searching (return starting index, or -1 if not found)
    int Find( const wxChar *pszSub ) const;             // like strstr
    // replace first (or all of bReplaceAll) occurences of substring with
    // another string, returns the number of replacements made
    size_t Replace( const wxChar *szOld,
                    const wxChar *szNew,
                    bool bReplaceAll = true );

    // check if the string contents matches a mask containing '*' and '?'
    bool Matches( const wxChar *szMask ) const;
    bool ToLong( long *val, int base = 10 ) const;
    // convert to an unsigned integer
    bool ToULong( unsigned long *val, int base = 10 ) const;
    // convert to wxLongLong
    #if defined(wxLongLong_t)
    bool ToLongLong( wxLongLong_t *val, int base = 10 ) const;
    // convert to wxULongLong
    bool ToULongLong( wxULongLong_t *val, int base = 10 ) const;
    #endif // wxLongLong_t
    // convert to a double
    bool ToDouble( double *val ) const;
    int Printf( const wxChar *pszFormat, ... ) ATTRIBUTE_PRINTF_2;
    // as vprintf(), returns the number of characters written or < 0 on error
    int PrintfV( const wxChar* pszFormat, va_list argptr );

    // returns the string containing the result of Printf() to it
    static wxString Format( const wxChar *pszFormat, ... ) ATTRIBUTE_PRINTF_1;
    // the same as above, but takes a va_list
    static wxString FormatV( const wxChar *pszFormat, va_list argptr );
    bool Alloc( size_t nLen ) { reserve( nLen ); /*return capacity() >= nLen;*/ return true; }
    // minimize the string's memory
    // only works if the data of this string is not shared
    bool Shrink();
    #if !wxUSE_STL
    wxChar *GetWriteBuf( size_t nLen );
    // call this immediately after GetWriteBuf() has been used
    void UngetWriteBuf();
    void UngetWriteBuf( size_t nLen );
    #endif

    // wxWidgets version 1 compatibility functions

    // use Mid()
    wxString SubString( size_t from, size_t to ) const { return Mid( from, ( to - from + 1 ) ); }
    // values for second parameter of CompareTo function
    enum caseCompare {exact, ignoreCase};
    // values for first parameter of Strip function
    enum stripType {leading = 0x1, trailing = 0x2, both = 0x3};

    // use Printf()
    // (take 'this' into account in attribute parameter count)
    int sprintf( const wxChar *pszFormat, ... ) ATTRIBUTE_PRINTF_2;

    // use Cmp()
    inline int CompareTo( const wxChar* psz, caseCompare cmp = exact ) const { return cmp == exact ? Cmp( psz ) : CmpNoCase( psz ); }

    // use Len
    size_t Length() const { return length(); }
    // Count the number of characters
    int Freq( wxChar ch ) const;
    // use MakeLower
    void LowerCase() { MakeLower(); }
    // use MakeUpper
    void UpperCase() { MakeUpper(); }
    // use Trim except that it doesn't change this string
    wxString Strip( stripType w = trailing ) const;

    // use Find (more general variants not yet supported)
    size_t Index( const wxChar* psz ) const { return Find( psz ); }
    size_t Index( wxChar ch )         const { return Find( ch );  }
    // use Truncate
    wxString& Remove( size_t pos ) { return Truncate( pos ); }
    wxString& RemoveLast( size_t n = 1 ) { return Truncate( length() - n ); }

    wxString& Remove( size_t nStart, size_t nLen ) { return ( wxString& )erase( nStart, nLen ); }

    // use Find()
    int First( const wxChar ch ) const { return Find( ch ); }
    int First( const wxChar* psz ) const { return Find( psz ); }
    int First( const wxString &str ) const { return Find( str ); }
    int Last( const wxChar ch ) const { return Find( ch, true ); }
    bool Contains( const wxString& str ) const { return Find( str ) != wxNOT_FOUND; }

    // use empty()
    bool IsNull() const { return empty(); }

    // std::string compatibility functions

    // take nLen chars starting at nPos
    wxString( const wxString& str, size_t nPos, size_t nLen )
      : wxStringBase( str, nPos, nLen ) { }
    // take all characters from pStart to pEnd
    wxString( const void *pStart, const void *pEnd )
      : wxStringBase( ( const wxChar* )pStart, ( const wxChar* )pEnd ) { }

    // lib.string.modifiers
    // append elements str[pos], ..., str[pos+n]
    wxString& append( const wxString& str, size_t pos, size_t n ) { return ( wxString& )wxStringBase::append( str, pos, n ); }
    // append a string
    wxString& append( const wxString& str ) { return ( wxString& )wxStringBase::append( str ); }
    // append first n (or all if n == npos) characters of sz
    wxString& append( const wxChar *sz ) { return ( wxString& )wxStringBase::append( sz ); }
    wxString& append( const wxChar *sz, size_t n ) { return ( wxString& )wxStringBase::append( sz, n ); }
    // append n copies of ch
    wxString& append( size_t n, wxChar ch ) { return ( wxString& )wxStringBase::append( n, ch ); }
    // append from first to last
    wxString& append( const_iterator first, const_iterator last ) { return ( wxString& )wxStringBase::append( first, last ); }

    // same as `this_string = str'
    wxString& assign( const wxString& str ) { return ( wxString& )wxStringBase::assign( str ); }
    // same as ` = str[pos..pos + n]
    wxString& assign( const wxString& str, size_t pos, size_t n ) { return ( wxString& )wxStringBase::assign( str, pos, n ); }
    // same as `= first n (or all if n == npos) characters of sz'
    wxString& assign( const wxChar *sz ) { return ( wxString& )wxStringBase::assign( sz ); }
    wxString& assign( const wxChar *sz, size_t n ) { return ( wxString& )wxStringBase::assign( sz, n ); }
    // same as `= n copies of ch'
    wxString& assign( size_t n, wxChar ch ) { return ( wxString& )wxStringBase::assign( n, ch ); }
    // assign from first to last
    wxString& assign( const_iterator first, const_iterator last ) { return ( wxString& )wxStringBase::assign( first, last ); }

    // string comparison
    #if !defined(HAVE_STD_STRING_COMPARE)
    int compare( const wxStringBase& str ) const;
    // comparison with a substring
    int compare( size_t nStart, size_t nLen, const wxStringBase& str ) const;
    // comparison of 2 substrings
    int compare( size_t nStart, size_t nLen,
                 const wxStringBase& str, size_t nStart2, size_t nLen2 ) const;
    // just like strcmp()
    int compare( const wxChar* sz ) const;
    // substring comparison with first nCount characters of sz
    int compare( size_t nStart, size_t nLen,
                 const wxChar* sz, size_t nCount = npos ) const;
    #endif // !defined HAVE_STD_STRING_COMPARE

    // insert another string
    wxString& insert( size_t nPos, const wxString& str ) { return ( wxString& )wxStringBase::insert( nPos, str ); }
    // insert n chars of str starting at nStart (in str)
    wxString& insert( size_t nPos, const wxString& str, size_t nStart, size_t n ) { return ( wxString& )wxStringBase::insert( nPos, str, nStart, n ); }
    // insert first n (or all if n == npos) characters of sz
    wxString& insert( size_t nPos, const wxChar *sz ) { return ( wxString& )wxStringBase::insert( nPos, sz ); }
    wxString& insert( size_t nPos, const wxChar *sz, size_t n ) { return ( wxString& )wxStringBase::insert( nPos, sz, n ); }
    // insert n copies of ch
    wxString& insert( size_t nPos, size_t n, wxChar ch ) { return ( wxString& )wxStringBase::insert( nPos, n, ch ); }
    iterator insert( iterator it, wxChar ch ) { return wxStringBase::insert( it, ch ); }
    void insert( iterator it, const_iterator first, const_iterator last ) { wxStringBase::insert( it, first, last ); }
    void insert( iterator it, size_type n, wxChar ch ) { wxStringBase::insert( it, n, ch ); }

    // delete characters from nStart to nStart + nLen
    wxString& erase( size_type pos = 0, size_type n = npos ) { return ( wxString& )wxStringBase::erase( pos, n ); }
    iterator erase( iterator first, iterator last ) { return wxStringBase::erase( first, last ); }
    iterator erase( iterator first ) { return wxStringBase::erase( first ); }

    #ifdef wxSTRING_BASE_HASNT_CLEAR
    void clear() { erase(); }
    #endif

    // replaces the substring of length nLen starting at nStart
    wxString& replace( size_t nStart, size_t nLen, const wxChar* sz ) { return ( wxString& )wxStringBase::replace( nStart, nLen, sz ); }
    // replaces the substring of length nLen starting at nStart
    wxString& replace( size_t nStart, size_t nLen, const wxString& str ) { return ( wxString& )wxStringBase::replace( nStart, nLen, str ); }
    // replaces the substring with nCount copies of ch
    wxString& replace( size_t nStart, size_t nLen, size_t nCount, wxChar ch ) { return ( wxString& )wxStringBase::replace( nStart, nLen, nCount, ch ); }
    // replaces a substring with another substring
    wxString& replace( size_t nStart, size_t nLen,
                       const wxString& str, size_t nStart2, size_t nLen2 ) {
      return ( wxString& )wxStringBase::replace( nStart, nLen, str,
             nStart2, nLen2 );
    }
    // replaces the substring with first nCount chars of sz
    wxString& replace( size_t nStart, size_t nLen,
                       const wxChar* sz, size_t nCount ) { return ( wxString& )wxStringBase::replace( nStart, nLen, sz, nCount ); }
    wxString& replace( iterator first, iterator last, const_pointer s ) { return ( wxString& )wxStringBase::replace( first, last, s ); }
    wxString& replace( iterator first, iterator last, const_pointer s,
                       size_type n ) { return ( wxString& )wxStringBase::replace( first, last, s, n ); }
    wxString& replace( iterator first, iterator last, const wxString& s ) { return ( wxString& )wxStringBase::replace( first, last, s ); }
    wxString& replace( iterator first, iterator last, size_type n, wxChar c ) { return ( wxString& )wxStringBase::replace( first, last, n, c ); }
    wxString& replace( iterator first, iterator last,
                       const_iterator first1, const_iterator last1 ) { return ( wxString& )wxStringBase::replace( first, last, first1, last1 ); }

    // string += string
    wxString& operator+=( const wxString& s ) { return ( wxString& )wxStringBase::operator+=( s ); }
    // string += C string
    wxString& operator+=( const wxChar *psz ) { return ( wxString& )wxStringBase::operator+=( psz ); }
    // string += char
    wxString& operator+=( wxChar ch ) { return ( wxString& )wxStringBase::operator+=( ch ); }
};

wxString operator+( const wxString& string1,  const wxString& string2 );
wxString operator+( const wxString& string, wxChar ch );
wxString operator+( wxChar ch, const wxString& string );
wxString operator+( const wxString& string, const wxChar *psz );
wxString operator+( const wxChar *psz, const wxString& string );


// 返回空的wxString（比这里的wxString（）更有效）
inline const wxString& wxGetEmptyString() {
  return *( wxString * )&wxEmptyString;
}

class wxStringBuffer {
  public:
    wxStringBuffer( wxString& str, size_t lenWanted = 1024 )
      : m_str( str ), m_buf( NULL ) { m_buf = m_str.GetWriteBuf( lenWanted ); }

    ~wxStringBuffer() { m_str.UngetWriteBuf(); }

    operator wxChar*() const { return m_buf; }

  private:
    wxString& m_str;
    wxChar   *m_buf;

    DECLARE_NO_COPY_CLASS( wxStringBuffer )
};

class wxStringBufferLength {
  public:
    wxStringBufferLength( wxString& str, size_t lenWanted = 1024 )
      : m_str( str ), m_buf( NULL ), m_len( 0 ), m_lenSet( false ) {
      m_buf = m_str.GetWriteBuf( lenWanted );
      wxASSERT( m_buf != NULL );
    }

    ~wxStringBufferLength() {
      wxASSERT( m_lenSet );
      m_str.UngetWriteBuf( m_len );
    }

    operator wxChar*() const { return m_buf; }
    void SetLength( size_t length ) { m_len = length; m_lenSet = true; }

  private:
    wxString& m_str;
    wxChar   *m_buf;
    size_t    m_len;
    bool      m_lenSet;

    DECLARE_NO_COPY_CLASS( wxStringBufferLength )
};

inline bool operator==( const wxString& s1, const wxString& s2 ) {
  return ( s1.Len() == s2.Len() ) && ( s1.Cmp( s2 ) == 0 );
}
inline bool operator==( const wxString& s1, const wxChar  * s2 ) {
  return s1.Cmp( s2 ) == 0;
}
inline bool operator==( const wxChar  * s1, const wxString& s2 ) {
  return s2.Cmp( s1 ) == 0;
}
inline bool operator!=( const wxString& s1, const wxString& s2 ) {
  return ( s1.Len() != s2.Len() ) || ( s1.Cmp( s2 ) != 0 );
}
inline bool operator!=( const wxString& s1, const wxChar  * s2 ) {
  return s1.Cmp( s2 ) != 0;
}
inline bool operator!=( const wxChar  * s1, const wxString& s2 ) {
  return s2.Cmp( s1 ) != 0;
}
inline bool operator< ( const wxString& s1, const wxString& s2 ) {
  return s1.Cmp( s2 ) < 0;
}
inline bool operator< ( const wxString& s1, const wxChar  * s2 ) {
  return s1.Cmp( s2 ) <  0;
}
inline bool operator< ( const wxChar  * s1, const wxString& s2 ) {
  return s2.Cmp( s1 ) >  0;
}
inline bool operator> ( const wxString& s1, const wxString& s2 ) {
  return s1.Cmp( s2 ) >  0;
}
inline bool operator> ( const wxString& s1, const wxChar  * s2 ) {
  return s1.Cmp( s2 ) >  0;
}
inline bool operator> ( const wxChar  * s1, const wxString& s2 ) {
  return s2.Cmp( s1 ) <  0;
}
inline bool operator<=( const wxString& s1, const wxString& s2 ) {
  return s1.Cmp( s2 ) <= 0;
}
inline bool operator<=( const wxString& s1, const wxChar  * s2 ) {
  return s1.Cmp( s2 ) <= 0;
}
inline bool operator<=( const wxChar  * s1, const wxString& s2 ) {
  return s2.Cmp( s1 ) >= 0;
}
inline bool operator>=( const wxString& s1, const wxString& s2 ) {
  return s1.Cmp( s2 ) >= 0;
}
inline bool operator>=( const wxString& s1, const wxChar  * s2 ) {
  return s1.Cmp( s2 ) >= 0;
}
inline bool operator>=( const wxChar  * s1, const wxString& s2 ) {
  return s2.Cmp( s1 ) <= 0;
}

inline bool operator==( const wxString& s1, const wxWCharBuffer& s2 ) {
  return ( s1.Cmp( ( const wchar_t * )s2 ) == 0 );
}
inline bool operator==( const wxWCharBuffer& s1, const wxString& s2 ) {
  return ( s2.Cmp( ( const wchar_t * )s1 ) == 0 );
}
inline bool operator!=( const wxString& s1, const wxWCharBuffer& s2 ) {
  return ( s1.Cmp( ( const wchar_t * )s2 ) != 0 );
}
inline bool operator!=( const wxWCharBuffer& s1, const wxString& s2 ) {
  return ( s2.Cmp( ( const wchar_t * )s1 ) != 0 );
}
inline wxString operator+( const wxString& string, const wxWCharBuffer& buf ) {
  return string + ( const wchar_t * )buf;
}
inline wxString operator+( const wxWCharBuffer& buf, const wxString& string ) {
  return ( const wchar_t * )buf + string;
}

inline bool operator==( wxChar c, const wxString& s ) { return s.IsSameAs( c ); }
inline bool operator==( const wxString& s, wxChar c ) { return s.IsSameAs( c ); }
inline bool operator!=( wxChar c, const wxString& s ) { return !s.IsSameAs( c ); }
inline bool operator!=( const wxString& s, wxChar c ) { return !s.IsSameAs( c ); }

#undef wxASSERT_VALID_INDEX

std::ostream& operator<<( std::ostream&, const wxString& );

#endif
